#region "Copyright (C) Lenny Granovsky. 2011-2013"
//    This library provides extended cache capabilities to the ASP.NET applications.
//
//                Copyright (C) Lenny Granovsky. 2011-2013. 
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 2 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//    This program comes with ABSOLUTELY NO WARRANTY.
//    This is free software, and you are welcome to redistribute it
//    under certain conditions;
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Data;
using System.Data.SqlClient;

namespace FlexiCache.Utilities
{
    /// <summary>
    /// Class that allows to find and store an ASP.NET ApplicationId that ASP.NET Session management uses to append to an ASP.NET SessionId when uses SQLServer Session State management mode.
    /// </summary>
    internal sealed class ApplicationIdentification
    {
        /// <summary>
        /// Application variable name that is used by this class.
        /// </summary>
        public const string ApplicationIdStorageName = "CurrentAppId";
        /// <summary>
        /// Internal flag that controls an exception throwning when Application ID not found.
        /// </summary>
        public const bool ThrowExceptionIfNotFound = false;
        /// <summary>
        /// Default length of ASP.NET SessionId
        /// </summary>
        public const int SessionIdLength = 24;
        /// <summary>
        /// Default length of ASP.NET ApplicationId that ASP.NET Session management uses to append to an ASP.NET SessionId when uses SQLServer Session State management mode.
        /// </summary>
        public const int ApplicationIdLength = 8;

        private ApplicationIdentification()
        {
        }

        /// <summary>
        /// Gets Application Identifier that ASP.NET Session management uses to append to an ASP.NET SessionId when uses SQLServer Session State management mode. 
        /// Gets an empty string if Application ID not found and ThrowExceptionIfNotFound flag is false. 
        /// Throwns an exception if Application ID not found and ThrowExceptionIfNotFound is set to true, or if any other error was accured.
        /// </summary>
        public static string CurrentApplicationId
        {
            get
            {
                if (HttpContext.Current == null)
                    return "";
                if (HttpContext.Current.Application[ApplicationIdStorageName] != null)
                    return HttpContext.Current.Application[ApplicationIdStorageName].ToString();

                try
                {
                    HttpContext.Current.Application.Lock();
                    if (HttpContext.Current.Application[ApplicationIdStorageName] != null)
                        return HttpContext.Current.Application[ApplicationIdStorageName].ToString();

                    HttpContext.Current.Application.Add(ApplicationIdStorageName, BuildAppId());
                    return HttpContext.Current.Application[ApplicationIdStorageName].ToString();
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    HttpContext.Current.Application.UnLock();
                }
            }
        }

        /// <summary>
        /// Clears current ApplicationID cache.
        /// </summary>
        /// <remarks>This method temporarily locks the Application Content variables collection.</remarks>
        public static void ClearCurrentApplicationId()
        {
            if (HttpContext.Current != null)
            {
                try
                {
                    HttpContext.Current.Application.Lock();
                    HttpContext.Current.Application.Remove(ApplicationIdentification.ApplicationIdStorageName);
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    HttpContext.Current.Application.UnLock();
                }
            }
        }

        /// <summary>
        /// Gets full friendly Application name that ASP.NET runtime gets from IIS configuration/metadata.
        /// </summary>
        public static string FullName
        {
            get
            {
                return System.AppDomain.CurrentDomain.FriendlyName;
            }
        }

        /// <summary>
        /// Gets just a portion of full Application name that ASP.NET runtime gets from IIS configuration/metadata and uses to store and identify session's data in SQLServer Session State management mode.
        /// </summary>
        public static string Name
        {
            get
            {
			    string appName = System.AppDomain.CurrentDomain.FriendlyName;
                if (appName.Contains("-"))
                    appName = appName.Substring(0, appName.IndexOf("-"));
                return appName;
            }
        }

        /// <summary>
        /// An internal method that builds an ApplicationId from ApplicationName by using the same method that MS uses in DB.
        /// </summary>
        /// <returns>Returns string that represents an ApplicationId</returns>
        private static string BuildAppId()
        {
            string appName = ApplicationIdentification.Name;
            if (appName.Length == 0)
            {
                if (ThrowExceptionIfNotFound)
                    throw new ApplicationException("Application Id cannot be calculated for an unknown application.");
                else return string.Empty;
            }

            int hash = 5381;
            int len = appName.Length;
            appName = appName.ToLower();
            int c = 0;
            for (int i = 0; i < len; i++)
            {
                c = Convert.ToInt32(appName[i]);
                hash = ((hash << 5) + hash) ^ c;
            }
            string hexAppId = String.Format("{0:x}", hash);
            return hexAppId.PadLeft(ApplicationIdentification.ApplicationIdLength, '0');
        }

        /// <summary>
        /// Returns Session Identifier that ASP.NET Session management uses to store and identify session's data in dbo.ASPStateTempSessions table in SQLServer Session State management mode.
        /// </summary>
        /// <param name="aspSessionId">ASP.NET SessionId string.</param>
        /// <returns>Returns string that ASP.NET Session management uses to store and identify session's data in dbo.ASPStateTempSessions table.</returns>
        public static string GetStorageSessionId(string aspSessionId)
        {
            return string.Format("{0}{1}", aspSessionId, CurrentApplicationId);
        }
    }
}
